﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SharedGenomics.Domain
{
    public class CollectionIndex<T>
    {
        private IDictionary<T, int> _lookupIndex;
        private IList<T> _items;

        public CollectionIndex()
        {
            this._lookupIndex = new Dictionary<T, int>();
            this._items = new List<T>();
        }

        public bool Contains(T item)
        {
            return this.Find(item) != -1;
        }

        public void ItemInserted(T item)
        {
            this.ItemInsertedAt(item, this.Count);
        }

        public void ItemInsertedAt(T item, int index)
        {
            if (index < 0 || index > this.Count)
                throw new ArgumentOutOfRangeException("index", "Item insert index must be in the range [0, index.Count]");

            if(this._lookupIndex.ContainsKey(item))
                throw new ArgumentException("This item is already contained in the index");

            this._lookupIndex.Add(item, index);
            this._items.Insert(index, item);

            this.UpdatedIndexesFrom(index + 1);
        }

        public bool ItemRemoved(T item)
        {
            int index = this.Find(item);

            if (index >= 0)
            {
                T removedItem = this.ItemRemovedAt(index);
                return true;
            }
            else
                return false;
        }

        public T ItemRemovedAt(int index)
        {
            if (index < 0 || index >= this.Count)
                throw new IndexOutOfRangeException("Index is out of range for the collection");

            T removedItem = this._items[index];
            this._items.RemoveAt(index);
            this._lookupIndex.Remove(removedItem);

            //update the indexes of all items after the removed item - NOTE: the start index is removal index since the following item will now occupy that 
            //index
            this.UpdatedIndexesFrom(index);

            return removedItem;
        }

        public void Clear()
        {
            this._items.Clear();
            this._lookupIndex.Clear();
        }

        public int Find(T item)
        {
            return this._lookupIndex.ContainsKey(item) ? this._lookupIndex[item] : -1;
        }

        private void UpdatedIndexesFrom(int startIndex)
        {
            //after the insert, every item after the inserted item has an invalid index - update the indices of these items
            for (int i = startIndex; i < this._items.Count; i++)
            {
                this._lookupIndex[this._items[i]] = i;
            }
        }

        public int Count
        {
            get { return this._items.Count; }
        }
    }
}
